// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.designer.ui.properties;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dengues.core.DenguesCorePlugin;
import org.dengues.core.property.PropertyTitleLabelProvider;
import org.dengues.core.warehouse.IWarehouseNode;
import org.dengues.core.warehouse.IWarehouseView;
import org.dengues.designer.ui.database.parts.DBColumnEditPart;
import org.dengues.designer.ui.database.parts.DBDiagramEditPart;
import org.dengues.designer.ui.database.parts.DBTableEditPart;
import org.dengues.designer.ui.editors.DatabaseMultiEditor;
import org.dengues.designer.ui.editors.ProcessMultiEditor;
import org.dengues.designer.ui.i18n.Messages;
import org.dengues.designer.ui.process.parts.CompConnectionPart;
import org.dengues.designer.ui.process.parts.CompNodePart;
import org.dengues.designer.ui.process.parts.CompProcessPart;
import org.dengues.designer.ui.properties.tabbed.DenguesTab;
import org.dengues.designer.ui.properties.tabbed.DenguesTabDescriptor;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyComposite;
import org.eclipse.ui.internal.views.properties.tabbed.view.TabbedPropertyViewer;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.views.properties.tabbed.ISection;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;

/**
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf Qiang.Zhang.Adolf@gmail.com 2008-1-15 qiang.zhang $
 * 
 */
public class ComponentPropertyView extends ViewPart implements ISelectionListener, ISelectionChangedListener {

    private final PropertyTitleLabelProvider labelProvider;

    private TabbedPropertySheetWidgetFactory widgetFactory;

    private TabbedPropertyComposite tabbedPropertyComposite;

    private final boolean hasTitleBar;

    private TabbedPropertyViewer tabbedPropertyViewer;

    private HashMap<DenguesTab, Composite> tabToComposite;

    private Map<DenguesTabDescriptor, DenguesTab> descriptorToTab;

    private DenguesTab currentTab;

    private List<String> selectionQueue;

    private boolean selectionQueueLocked;

    private boolean selectedPrimary;

    private final IContentProvider tabListContentProvider = new IStructuredContentProvider() {

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.IContentProvider#dispose()
         */
        public void dispose() {

        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
         */
        public Object[] getElements(Object inputElement) {
            if (inputElement instanceof IStructuredSelection) {
                IStructuredSelection selection = (IStructuredSelection) inputElement;
                return getDenguesTabDescriptors(selection);
            }
            return new Object[0];
        }

        /*
         * (non-Javadoc)
         * 
         * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
         * java.lang.Object, java.lang.Object)
         */
        public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        }
    };

    private DenguesTabDescriptor[] descriptors;

    /**
     * Qiang.Zhang.Adolf@gmail.com ComponentPropertyView constructor comment.
     */
    public ComponentPropertyView() {
        labelProvider = new PropertyTitleLabelProvider();
        hasTitleBar = true;
        tabToComposite = new HashMap<DenguesTab, Composite>();
        descriptorToTab = new HashMap<DenguesTabDescriptor, DenguesTab>();
        selectionQueue = new ArrayList<String>();
        descriptors = new DenguesTabDescriptor[0];
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getDenguesTabDescriptors".
     * 
     * @param selection
     * @return
     */
    protected DenguesTabDescriptor[] getDenguesTabDescriptors(IStructuredSelection selection) {
        Object firstElement = selection.getFirstElement();
        if (firstElement instanceof CompNodePart || firstElement instanceof DBColumnEditPart
                || firstElement instanceof DBTableEditPart) {
            descriptors = new DenguesTabDescriptor[2];
            DenguesTab denguesTab;
            createMainTab();

            descriptors[1] = new DenguesTabDescriptor();
            descriptors[1].setText(Messages.getString("ComponentPropertyView.property")); //$NON-NLS-1$

            denguesTab = new DenguesTab();
            PropertiesTabSection propertiesTabSection = new PropertiesTabSection(widgetFactory);
            denguesTab.setSections(new ISection[] { propertiesTabSection });
            descriptorToTab.put(descriptors[1], denguesTab);
        } else if (firstElement instanceof CompConnectionPart) {
            descriptors = new DenguesTabDescriptor[1];
            createMainTab();
        } else {
            if (firstElement instanceof CompProcessPart) {
                firstElement = ((CompProcessPart) firstElement).getAdapter(IWarehouseNode.class);
            }
            if (firstElement instanceof IWarehouseNode || firstElement instanceof DBDiagramEditPart) {
                descriptors = new DenguesTabDescriptor[1];
                descriptors[0] = new DenguesTabDescriptor();
                descriptors[0].setText(Messages.getString("ComponentPropertyView.main")); //$NON-NLS-1$
                DenguesTab denguesTab = new DenguesTab();
                ISection mainTabSection = DenguesCorePlugin.getDefault().getWarehousePluginService().getDescriptionSection(
                        widgetFactory);
                denguesTab.setSections(new ISection[] { mainTabSection });
                descriptorToTab.put(descriptors[0], denguesTab);
            }
        }
        return descriptors;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "createGefMainTab".
     */
    private void createMainTab() {
        descriptors[0] = new DenguesTabDescriptor();
        descriptors[0].setText(Messages.getString("ComponentPropertyView.main")); //$NON-NLS-1$

        DenguesTab denguesTab = new DenguesTab();
        MainTabSection mainTabSection = new MainTabSection(widgetFactory);
        denguesTab.setSections(new ISection[] { mainTabSection });
        descriptorToTab.put(descriptors[0], denguesTab);
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
     */
    @Override
    public void createPartControl(Composite parent) {
        getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(this);
        widgetFactory = new TabbedPropertySheetWidgetFactory();
        tabbedPropertyComposite = new TabbedPropertyComposite(parent, widgetFactory, hasTitleBar);
        widgetFactory.paintBordersFor(tabbedPropertyComposite);
        tabbedPropertyComposite.setLayout(new FormLayout());
        FormData formData = new FormData();
        formData.left = new FormAttachment(0, 0);
        formData.right = new FormAttachment(100, 0);
        formData.top = new FormAttachment(0, 0);
        formData.bottom = new FormAttachment(100, 0);
        tabbedPropertyComposite.setLayoutData(formData);

        tabbedPropertyViewer = new TabbedPropertyViewer(tabbedPropertyComposite.getList());
        tabbedPropertyViewer.setContentProvider(tabListContentProvider);
        tabbedPropertyViewer.setLabelProvider(labelProvider);
        tabbedPropertyViewer.addSelectionChangedListener(this);

        /**
         * Add a part activation listener.
         */
        // cachedWorkbenchWindow = getSite().getWorkbenchWindow();
        // cachedWorkbenchWindow.getPartService().addPartListener(
        // this);
        /**
         * Add a label provider change listener.
         */
        // if (hasTitleBar) {
        // registry.getLabelProvider().addListener(this);
        // }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
     */
    @Override
    public void setFocus() {
        if (selectedPrimary) {
            if (getViewSite() != null) {
                getViewSite().getShell().setFocus();
            }
        } else {
            if (tabbedPropertyComposite != null) {
                tabbedPropertyComposite.setFocus();
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.part.WorkbenchPart#dispose()
     */
    @Override
    public void dispose() {
        getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(this);
        if (widgetFactory != null) {
            widgetFactory.dispose();
            widgetFactory = null;
        }
        /**
         * If the current tab is about to be disposed we have to call aboutToBeHidden
         */
        if (currentTab != null) {
            currentTab.aboutToBeHidden();
            currentTab = null;
        }
        disposeTabs(descriptorToTab.values());
        tabbedPropertyComposite = null;
        tabToComposite = new HashMap<DenguesTab, Composite>();
        descriptorToTab = new HashMap<DenguesTabDescriptor, DenguesTab>();
        selectionQueue = new ArrayList<String>();
        super.dispose();
    }

    private void disposeTabs(Collection<DenguesTab> tabs) {
        for (Iterator<DenguesTab> iter = tabs.iterator(); iter.hasNext();) {
            DenguesTab tab = iter.next();
            Composite composite = tabToComposite.remove(tab);
            tab.dispose();
            if (composite != null) {
                composite.dispose();
            }
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.ui.ISelectionListener#selectionChanged(org.eclipse.ui.IWorkbenchPart,
     * org.eclipse.jface.viewers.ISelection)
     */
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (!(selection instanceof IStructuredSelection)) {
            clearAll();
            return;
        }
        String text = null;
        Image image = null;
        IStructuredSelection structuredSelection = (IStructuredSelection) selection;
        if ((part instanceof ProcessMultiEditor) || part instanceof DatabaseMultiEditor) {
            text = labelProvider.getText(structuredSelection);
            image = labelProvider.getImage(structuredSelection);
        } else if (part instanceof IWarehouseView) {
            text = labelProvider.getText(structuredSelection.getFirstElement());
            image = labelProvider.getImage(structuredSelection.getFirstElement());
        }
        boolean isSupport = text != null && !"".equals(text); //$NON-NLS-1$
        if (isSupport) {
            tabbedPropertyComposite.getTitle().setTitle(text, image);
            tabbedPropertyViewer.setInput(part, structuredSelection);
            if (descriptors.length > 0) {
                tabbedPropertyViewer.setSelection(new StructuredSelection(descriptors[0]), isSupport);
            }
        } else {
            clearAll();
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "clearAll".
     */
    public void clearAll() {
        tabbedPropertyComposite.getTitle().setTitle(null, null);
        tabbedPropertyViewer.setInput(null);
        if (currentTab != null) {
            currentTab.aboutToBeHidden();
            currentTab = null;
        }
        disposeTabs(descriptorToTab.values());
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
     */
    public void selectionChanged(SelectionChangedEvent event) {
        IStructuredSelection selection = (IStructuredSelection) event.getSelection();
        Object firstElement = selection.getFirstElement();
        DenguesTabDescriptor descriptor = (DenguesTabDescriptor) selection.getFirstElement();
        DenguesTab tab = null;
        if (descriptor == null) {
            // pretend the tab is empty.
            hideTab(currentTab);
        } else {
            // create tab if necessary
            // can not cache based on the id - tabs may have the same id,
            // but different section depending on the selection
            tab = descriptorToTab.get(descriptor);

            if (tab != currentTab) {
                hideTab(currentTab);
            }

            Composite tabComposite = tabToComposite.get(tab);
            if (tabComposite == null) {
                tabComposite = createTabComposite();
                tab.createControls(tabComposite, widgetFactory);
                // tabAreaComposite.layout(true);
                tabToComposite.put(tab, tabComposite);
            }
            // force widgets to be resized
            tab.setInput(tabbedPropertyViewer.getWorkbenchPart(), (ISelection) tabbedPropertyViewer.getInput());

            // store tab selection
            storeCurrentTabSelection(descriptor.getText());

            if (tab != currentTab) {
                showTab(tab);
            }
            tab.refresh();
        }
        tabbedPropertyComposite.getTabComposite().layout(true, true);
        currentTab = tab;
        resizeScrolledComposite();

        if (descriptor != null) {
            handleTabSelection(descriptor);
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "resizeScrolledComposite".
     */
    private void resizeScrolledComposite() {
        Point currentTabSize = new Point(0, 0);
        if (currentTab != null) {
            Composite sizeReference = tabToComposite.get(currentTab);
            if (sizeReference != null) {
                currentTabSize = sizeReference.computeSize(SWT.DEFAULT, SWT.DEFAULT);
            }
        }
        tabbedPropertyComposite.getScrolledComposite().setMinSize(currentTabSize.x, currentTabSize.y);
    }

    /**
     * Handle the tab selected change event.
     * 
     * @param tabDescriptor the new selected tab.
     */
    private void handleTabSelection(DenguesTabDescriptor tabDescriptor) {
        if (selectionQueueLocked) {
            /*
             * don't send tab selection events for non user changes.
             */
            selectedPrimary = false;
            return;
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "storeCurrentTabSelection".
     * 
     * @param text
     */
    private void storeCurrentTabSelection(String text) {
        if (!selectionQueueLocked) {
            selectionQueue.remove(text);
            selectionQueue.add(0, text);
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "showTab".
     * 
     * @param target
     */
    private void showTab(DenguesTab target) {
        if (target != null) {
            Composite tabComposite = tabToComposite.get(target);
            if (tabComposite != null) {
                /**
                 * the following method call order is important - do not change it or the widgets might be drawn
                 * incorrectly
                 */
                tabComposite.moveAbove(null);
                target.aboutToBeShown();
                tabComposite.setVisible(true);
            }
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "hideTab".
     * 
     * @param target
     */
    private void hideTab(DenguesTab target) {
        if (target != null) {
            Composite tabComposite = tabToComposite.get(target);
            if (tabComposite != null) {
                target.aboutToBeHidden();
                tabComposite.setVisible(false);
            }
        }
    }

    /**
     * Helper method for creating property tab composites.
     */
    private Composite createTabComposite() {
        Composite result = widgetFactory.createComposite(tabbedPropertyComposite.getTabComposite(), SWT.NO_FOCUS);
        result.setVisible(false);
        result.setLayout(new FillLayout());
        FormData data = new FormData();
        if (hasTitleBar) {
            data.top = new FormAttachment(tabbedPropertyComposite.getTitle(), 0);
        } else {
            data.top = new FormAttachment(0, 0);
        }
        data.bottom = new FormAttachment(100, 0);
        data.left = new FormAttachment(0, 0);
        data.right = new FormAttachment(100, 0);
        result.setLayoutData(data);
        return result;
    }
}
